package cn.com.rexen.rplus.controller;

import cn.com.rexen.rplus.core.criteria.Criteria;
import cn.com.rexen.rplus.core.criteria.Page;
import cn.com.rexen.rplus.core.criteria.PageRequest;
import cn.com.rexen.rplus.core.criteria.Pageable;
import cn.com.rexen.rplus.core.execption.EmptyValueException;
import cn.com.rexen.rplus.sa.model.BaseVO;
import cn.com.rexen.rplus.sa.model.PageVO;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindException;
import org.springframework.validation.Validator;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.List;

/**
 * 增删改查基础Controller.
 *
 * @param <T> 泛型支持.
 * @version 1.0.0
 */
public abstract class CURDControllerSupport<T extends Serializable, V extends BaseVO> extends AbstractCURDControllerSupport<T, V> {

    /**
     * 默认的每页显示条数.
     */
    public static final int DEFAULT_PAGESIZE = 10;
    /**
     * 日志器.
     */
    protected static final Logger LOGGER = LoggerFactory.getLogger(CURDControllerSupport.class);

    /**
     * 根结点标志.
     */
    protected static final String ROOT = "_root";

    /**
     * 实体名称.
     */
    private String entityName;
    /**
     * 验证器接口.
     */
    @Resource
    private Validator validator;

    /**
     * 获取实体的Class.
     *
     * @return class.
     */
    protected Class<T> getEntityClass() {
        return (Class<T>) getEntity().getClass();
    }


    /**
     * 根据默认的请求参数进行分页查询。 回调函数：beforeDoList(...), afterDoList(...).
     *
     * @param request  http请求.
     * @param response http响应.
     * @return ModelMap.
     */
    @RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
    @ResponseBody
    public ModelMap list(HttpServletRequest request, HttpServletResponse response) {
        ModelMap modelMap = new ModelMap();
        try {
            beforeList(request, response);
            Page<T> page = doList(request, response);
            afterList(request, response, modelMap, page);

            //转换实体类型
            List<V> voList = entityConvertVoInList(page.getContent());
            modelMap.addAttribute("data", voList);
            modelMap.addAttribute("page", page2Vo(page));
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
            responseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), response);
        }
        return modelMap;
    }

    /**
     * page 转换成pageVo
     *
     * @param page
     * @return
     */
    protected PageVO page2Vo(Page<T> page) {
        PageVO pageVO = new PageVO(page.getTotalPages(), page.getNumber(), page.getTotalElements());
        return pageVO;
    }

    protected Page<T> doList(HttpServletRequest request, HttpServletResponse response) {
        Criteria criteria = getEntityService().getCriteria(getEntityClass());
        String pageSize = request.getParameter("pageSize");
        String page = request.getParameter("page");
        Pageable pageable = new PageRequest(page == null ? 0 : Integer.parseInt(page), pageSize == null ? DEFAULT_PAGESIZE
                : Integer.parseInt(pageSize));
        criteria.setPageable(pageable);

        //设置查询条件，由子类扩展实现.
        buildCriteria(criteria, request);

        Page<T> pageResult = getEntityService().findWithPage(criteria);

        return pageResult;
    }

    /**
     * 由子类扩展实现的查询条件.可以在此方法中增加查询的条件.
     *
     * @param criteria 查询条件，已存在分页信息.
     * @param request  HttpServletRequest
     */
    protected void buildCriteria(Criteria criteria, HttpServletRequest request) {
    }

    /**
     * 保存实体<br/>
     * 回调函数：beforeDoSave(...), afterDoSave(...). (目前还没有做jsr303验证)
     *
     * @param request  http请求.
     * @param response http响应.
     * @param vo       页面传入的VO对象.
     * @return vo .
     */
    @RequestMapping(value = "/save", method = RequestMethod.POST, headers = {"Content-type=application/json"})
    @ResponseBody
    public V save(HttpServletRequest request, HttpServletResponse response, @RequestBody V vo) {
        ModelMap mm = new ModelMap();
        try {
            BindException errors = bindRequestEntity(request, vo);
            beforeSave(request, response, vo, errors, mm);
            if (errors.hasErrors()) {
                // TODO 实现jsr303验证.
                LOGGER.error(errors.getMessage());
                responseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errors.getMessage(), response);
            }
            doSave(request, response);
            afterSave(request, response, mm, vo);
        } catch (Exception e) {
            LOGGER.error("保存时发生错误:{}", e);
            responseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), response);
        }
        return vo;
    }

    /**
     * 执行save方法.
     *
     * @param request  http请求.
     * @param response http响应.
     * @throws Exception 异常.
     */
    void doSave(HttpServletRequest request, HttpServletResponse response) throws Exception {
        getEntityService().save(getEntity());
    }

    /**
     * 修改实体<br/>
     * 回调函数：beforeUpdate(...), afterUpdate(...).
     *
     * @param request  http请求.
     * @param response http响应.
     * @param vo       页面传入的VO对象.
     * @param id       url地址id参数.
     * @return T vo对象.
     */
    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST, headers = {"Content-type=application/json"})
    @ResponseBody
    public V update(HttpServletRequest request, HttpServletResponse response, @PathVariable String id, @RequestBody V vo) {

        ModelMap mm = new ModelMap();
        try {
            if (StringUtils.isBlank(id)) {
                LOGGER.error("id 不能为空.");
                throw new EmptyValueException();
            }
            BindException errors = bindRequestEntity(request, vo);
            beforeUpdate(request, response, vo, errors, mm, id);
            if (errors.hasErrors()) {
                LOGGER.error(errors.getMessage());
                throw new Exception();
            }

            doUpdate(request, response);
        } catch (Exception e) {
            e.printStackTrace();
            responseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), response);
        }
        afterUpdate(request, response, mm, vo);
        return vo;
    }

    /**
     * 执行修改操作.
     *
     * @param request  http请求.
     * @param response http响应.
     * @throws Exception 异常.
     */
    public void doUpdate(HttpServletRequest request, HttpServletResponse response) throws Exception {
        getEntityService().saveOrUpdate(getEntity());
    }

    /**
     * 从Request中绑定对象并进行校验.
     *
     * @param request http请求.
     * @param entity  VO对象.
     * @return BindException .
     * @throws Exception 异常.
     */
    protected BindException bindRequestEntity(HttpServletRequest request, V entity) throws Exception {
        ServletRequestDataBinder binder = new ServletRequestDataBinder(entity);
        initBinder(binder);
        binder.bind(request);
        BindException errors = new BindException(binder.getBindingResult());
        validator.validate(entity, errors);
        return errors;
    }

    /**
     * 保存实体回调函数，在执行实体与Request参数绑定之前调用用。 注意：由于entity可能是托管对象，对entity所做的修改都将反映到数据库。
     * 所以有必要在此方法中进行前期的数据校验，以免发生意外.
     *
     * @param request http请求.
     * @param entity  VO对象.
     * @param mm      modelmap.
     * @return 是否通过校验
     */
    protected boolean beforeBindSaveRequestEntity(HttpServletRequest request, HttpServletResponse response, T entity, ModelMap mm) {
        return false;
    }

    /**
     * 修改实体回调函数，在执行实体与Request参数绑定之前调用用。 注意：由于entity可能是托管对象，对entity所做的修改都将反映到数据库。
     * 所以有必要在此方法中进行前期的数据校验，以免发生意外.
     *
     * @param request http请求.
     * @param entity  VO对象.
     * @param mm      modelmap.
     * @return 是否通过校验
     */
    protected boolean beforeBindUpdateRequestEntity(HttpServletRequest request, HttpServletResponse response, T entity, ModelMap mm) {
        return false;
    }

    /**
     * 保存实体(save)回调函数，在执行保存之前调用用。可以进行数据校验.
     *
     * @param request HttpServletRequest
     * @param entity  实体对象
     * @param errors  BindException 可以添加错误信息
     * @param mm      ModelAndView
     */
    protected void beforeSave(HttpServletRequest request, HttpServletResponse response, V entity, BindException errors, ModelMap mm) {
    }

    /**
     * 保存实体(save)回调函数，在返回视图之前调用用。可以继续添加返回信息.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param mm       ModelMap.
     * @param entity   VO对象.
     */
    protected void afterSave(HttpServletRequest request, HttpServletResponse response, ModelMap mm, V entity) {
    }

    /**
     * 保存实体(update)回调函数，在执行保存之前调用用。可以进行数据校验.
     *
     * @param request HttpServletRequest
     * @param entity  实体对象
     * @param errors  BindException 可以添加错误信息
     * @param mm      ModelAndView
     * @param id      .
     */
    protected void beforeUpdate(HttpServletRequest request, HttpServletResponse response, V entity, BindException errors, ModelMap mm, String id) {
    }

    /**
     * 保存实体(update)回调函数，在返回视图之前调用用。可以继续添加返回信息.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param mm       ModelMap.
     * @param vo       VO对象.
     */
    protected void afterUpdate(HttpServletRequest request, HttpServletResponse response, ModelMap mm, V vo) {
    }

    /**
     * 删除.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param id       url地址上用于删除的对象的id.
     * @return ModelMap.
     */
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    @ResponseBody
    public ModelMap delete(HttpServletRequest request, HttpServletResponse response, @PathVariable String id) {
        ModelMap mm = new ModelMap();
        try {
            if (StringUtils.isBlank(id)) {
                throw new EmptyValueException();
            }

            Object object = getEntityService().get(getEntityClass(), id);

            if (object == null) {
                throw new EmptyValueException("没有此对象.");
            }

            beforeRemove(request, response, object);
            doDelete(object);
            afterRemove(request, response, mm, new StringBuilder().append("成功删除").append("1个").append(entityName)
                    .append(" id：").append(id).toString());
            mm.addAttribute("msg", "删除成功.");
            mm.addAttribute("success", true);
        } catch (Exception e) {
            e.printStackTrace();
            responseError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage(), response);
        }
        return mm;
    }

    /**
     * 执行删除操作.
     *
     * @param object 删除对象的实体.
     * @throws Exception 异常.
     */
    public void doDelete(Object object) throws Exception {
        getEntityService().delete(object);
    }

    /**
     * 删除实体(remove.do)回调函数，在执行保存之前调用用。可以进行数据校验.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param object   实体对象
     */
    protected void beforeRemove(HttpServletRequest request, HttpServletResponse response, Object object) {
    }

    /**
     * 删除实体(remove.do)回调函数，在返回视图之前调用用。可以继续添加返回信息.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param mm       ModelMap.
     * @param msg      删除成功后的信息.
     */
    protected void afterRemove(HttpServletRequest request, HttpServletResponse response, ModelMap mm, String msg) {
    }

    /**
     * 查询实体信息<br/>
     *
     * @param request  http请求.
     * @param response http响应.
     * @param id       显详细信息的id.
     * @return 视图模型 .
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public V show(HttpServletRequest request, HttpServletResponse response, @PathVariable String id) {
        ModelMap mav = new ModelMap();
        T object = doShow(request, response, id, mav);
        V t = entityConvertVo(object);
        return t;
    }

    /**
     * 查询实体信息(show.do)回调函数，在返回视图之前调用用。可以继续添加返回信息.
     *
     * @param request  http请求.
     * @param response http响应.
     * @param id       实体对象
     * @param modelMap ModelMap.
     * @return VO对象.
     */
    @Override
    protected T doShow(HttpServletRequest request, HttpServletResponse response, Serializable id, ModelMap modelMap) {
        T t = (T) getEntityService().get(getEntityClass(), id);
        return t;
    }

    //	private String getMessageFromErrors(BindException errors) {
    //		StringBuilder sb = new StringBuilder();
    //		sb.append("错误信息：");
    //		List<ObjectError> list = errors.getAllErrors();
    //		for (ObjectError error : list) {
    //			sb.append(error.getDefaultMessage()).append(";");
    //		}
    //		return sb.toString();
    //	}

}
